#ifndef CMAT4_H
#define CMAT4_H
#include <stdio.h>
#include <string.h>
#include "CMath.h"
#if defined(QT_CREATOR)
#include <QtCore>
#endif
typedef float mat4[16];

#define degreesToRadians(x)(PI * x / 180.0)
class CMat4
{
public:
    CMat4();
    mat4 _mat;
    inline float* data(){
        return &_mat[0];
    }
    inline void Identity(){
        Identity(_mat);
    }
    inline static void Identity( mat4 m){
        m[0] = m[5] = m[10] = m[15] = 1.0f;
        m[1] = m[2] = m[3] = m[4] = 0.0f;
        m[6] = m[7] = m[8] = m[9] = 0.0f;
        m[11] = m[12] = m[13] = m[14] = 0.0f;
    }

    inline void Rotate(float angle, float vx, float vy, float vz){
        Rotate(_mat,angle,vx,vy,vz);
    }
    inline void Translate(float translatex, float translatey, float translatez){
        Translate(_mat,translatex,translatey,translatez);
    }
    inline void Scale(float scalex, float scaley, float scalez){
        Scale(_mat, scalex, scaley, scalez);
    }

    inline static void Rotate(mat4 m, float angle, float vx, float vy, float vz)
    {
        float len = sqrt(vx * vx + vy * vy + vz * vz);
        if(len != 0){
            vx /= len;
            vy /= len;
            vz /= len;
        }
        float c, s, ic;
        c = cos(angle * PI / 180.0);
        s = sin(angle * PI / 180.0);
        ic = 1.0f - c;
        float rot[16];
        rot[0 * 4 + 0] = vx * vx * ic + c;
        rot[1 * 4 + 0] = vx * vy * ic - vz * s;
        rot[2 * 4 + 0] = vx * vz * ic + vy * s;
        rot[3 * 4 + 0] = 0.0f;
        rot[0 * 4 + 1] = vy * vx * ic + vz * s;
        rot[1 * 4 + 1] = vy * vy * ic + c;
        rot[2 * 4 + 1] = vy * vz * ic - vx * s;
        rot[3 * 4 + 1] = 0.0f;
        rot[0 * 4 + 2] = vx * vz * ic - vy * s;
        rot[1 * 4 + 2] = vy * vz * ic + vx * s;
        rot[2 * 4 + 2] = vz * vz * ic + c;
        rot[3 * 4 + 2] = 0.0f;
        rot[0 * 4 + 3] = 0.0f;
        rot[1 * 4 + 3] = 0.0f;
        rot[2 * 4 + 3] = 0.0f;
        rot[3 * 4 + 3] = 1.0f;
        float temp[16];
        for(int i = 0; i < 4; ++i){
            for(int j = 0; j < 4; ++j){
                temp[j * 4 + i] = 0.0f;
                for(int k = 0; k < 4; ++k){
                    temp[j * 4 + i] += m[k * 4 + i] * rot[j * 4 + k];
                }
            }
        }
		memcpy(m, temp, sizeof(mat4));
    }
    inline static void Translate(mat4 m, float translatex, float translatey, float translatez)
    {
        m[3 * 4 + 0] += m[0 * 4 + 0] * translatex + m[1 * 4 + 0] * translatey + m[2 * 4 + 0] * translatez;
        m[3 * 4 + 1] += m[0 * 4 + 1] * translatex + m[1 * 4 + 1] * translatey + m[2 * 4 + 1] * translatez;
        m[3 * 4 + 2] += m[0 * 4 + 2] * translatex + m[1 * 4 + 2] * translatey + m[2 * 4 + 2] * translatez;
        m[3 * 4 + 3] += m[0 * 4 + 3] * translatex + m[1 * 4 + 3] * translatey + m[2 * 4 + 3] * translatez;
    }
    inline static void Scale(mat4 m, float scalex, float scaley, float scalez)
    {
        m[0] *= scalex;
        m[1] *= scalex;
        m[2] *= scalex;
        m[3] *= scalex;
        m[4] *= scaley;
        m[5] *= scaley;
        m[6] *= scaley;
        m[7] *= scaley;
        m[8] *= scalez;
        m[9] *= scalez;
        m[10] *= scalez;
        m[11] *= scalez;
    }
    char* toString();
    inline void Ortho(float left, float right, float bottom, float top,float near, float far){
        //Ortho(left,right,bottom,top,near,far,_mat);
    }
    static inline void Ortho(float left, float right, float bottom, float top,float near, float far, mat4 matrix)
    {
        // These paramaters are lens properties.
        // The "near" and "far" create the Depth of Field.
        // The "left", "right", "bottom" and "top" represent the rectangle formed
        // by the near area, this rectangle will also be the size of the visible area.
        //float near = 0.001, far = 100.0;
        //float left = 0.0, right = 320.0, bottom = 480.0, top = 0.0;

        // First Column
        matrix[0] = 2.0 /(right - left);
        matrix[1] = 0.0;
        matrix[2] = 0.0;
        matrix[3] = 0.0;

        // Second Column
        matrix[4] = 0.0;
        matrix[5] = 2.0 /(top - bottom);
        matrix[6] = 0.0;
        matrix[7] = 0.0;

        // Third Column
        matrix[8] = 0.0;
        matrix[9] = 0.0;
///        matrix[10] = -2.0 /(far - near);
        matrix[11] = 0.0;

        // Fourth Column
        matrix[12] =(right + left)/(right - left);
        matrix[13] =(top + bottom)/(top - bottom);
///        matrix[14] =(far + near)/(far - near);
        matrix[15] = 1;
    }
    inline void Perspective(float near, float far, float angle, float aspect){
///        Perspective(near,far,angle,aspect,_mat);
    }
    static inline void Perspective(float near, float far, float angle, float aspect, mat4 matrix)
    {
        // These paramaters are about lens properties.
        // The "near" and "far" create the Depth of Field.
        // The "angleOfView", as the name suggests, is the angle of view.
        // The "aspectRation" is the cool thing about this matrix. OpenGL doesn't
        // has any information about the screen you are rendering for. So the
        // results could seem stretched. But this variable puts the thing into the
        // right path. The aspect ration is your device screen(or desired area)width divided
        // by its height. This will give you a number < 1.0 the the area has more vertical
        // space and a number > 1.0 is the area has more horizontal space.
        // Aspect Ration of 1.0 represents a square area.
       // float near = 0.001, far = 100.0;
       // float angleOfView = 45.0;
       // float aspectRation = 0.75;

        // Some calculus before the formula.
        float size = 0;///near * tanf(degreesToRadians(angle)/ 2.0f);
        float left = -size, right = size, bottom = -size / aspect, top = size / aspect;

        // First Column
///        matrix[0] = 2 * (near / (right - left));
        matrix[1] = 0.0;
        matrix[2] = 0.0;
        matrix[3] = 0.0;

        // Second Column
        matrix[4] = 0.0;
///        matrix[5] = 2 * near /(top - bottom);
        matrix[6] = 0.0;
        matrix[7] = 0.0;

        // Third Column
        matrix[8] =(right + left)/(right - left);
        matrix[9] =(top + bottom)/(top - bottom);
///        matrix[10] = -(far + near)/(far - near);
        matrix[11] = -1;

        // Fourth Column
        matrix[12] = 0.0;
        matrix[13] = 0.0;
       /// matrix[14] = -(2 * far * near)/(far - near);
        matrix[15] = 0.0;
    }
    inline CMat4 operator*(CMat4 m){
        CMat4 temp;
        temp = *this * m._mat;
        return temp;
    }
    inline CMat4 operator*(mat4 m){
        CMat4 temp;
        // Fisrt Column
        temp._mat[0] = _mat[0]*m[0] + _mat[4]*m[1] + _mat[8]*m[2] + _mat[12]*m[3];
        temp._mat[1] = _mat[1]*m[0] + _mat[5]*m[1] + _mat[9]*m[2] + _mat[13]*m[3];
        temp._mat[2] = _mat[2]*m[0] + _mat[6]*m[1] + _mat[10]*m[2] + _mat[14]*m[3];
        temp._mat[3] = _mat[3]*m[0] + _mat[7]*m[1] + _mat[11]*m[2] + _mat[15]*m[3];

        // Second Column
        temp._mat[4] = _mat[0]*m[4] + _mat[4]*m[5] + _mat[8]*m[6] + _mat[12]*m[7];
        temp._mat[5] = _mat[1]*m[4] + _mat[5]*m[5] + _mat[9]*m[6] + _mat[13]*m[7];
        temp._mat[6] = _mat[2]*m[4] + _mat[6]*m[5] + _mat[10]*m[6] + _mat[14]*m[7];
        temp._mat[7] = _mat[3]*m[4] + _mat[7]*m[5] + _mat[11]*m[6] + _mat[15]*m[7];

        // Third Column
        temp._mat[8] = _mat[0]*m[8] + _mat[4]*m[9] + _mat[8]*m[10] + _mat[12]*m[11];
        temp._mat[9] = _mat[1]*m[8] + _mat[5]*m[9] + _mat[9]*m[10] + _mat[13]*m[11];
        temp._mat[10] = _mat[2]*m[8] + _mat[6]*m[9] + _mat[10]*m[10] + _mat[14]*m[11];
        temp._mat[11] = _mat[3]*m[8] + _mat[7]*m[9] + _mat[11]*m[10] + _mat[15]*m[11];

        // Fourth Column
        temp._mat[12] = _mat[0]*m[12] + _mat[4]*m[13] + _mat[8]*m[14] + _mat[12]*m[15];
        temp._mat[13] = _mat[1]*m[12] + _mat[5]*m[13] + _mat[9]*m[14] + _mat[13]*m[15];
        temp._mat[14] = _mat[2]*m[12] + _mat[6]*m[13] + _mat[10]*m[14] + _mat[14]*m[15];
        temp._mat[15] = _mat[3]*m[12] + _mat[7]*m[13] + _mat[11]*m[14] + _mat[15]*m[15];
        return temp;
    }
    inline CMat4 operator=(CMat4 m){
        memcpy(_mat, m._mat, sizeof(m._mat));
        return *this;
    }
    inline CMat4 operator=(mat4 m){
        memcpy(_mat, m, sizeof(m));
        return *this;
    }
};
#endif // MAT4_H
